package com.fecean.aabbc.commons.easy;

import java.time.*;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static java.time.temporal.ChronoField.*;

/**
 ** 通用日期时间操作类
 *
 * @author: <a href="mailto:chenfenghai@oristartech.com">chenfenghai</a>
 * @version: 1.0
 * @date: 2019-10-21 14:18
 */
public class EasyDate {

    /**
     ** 缓存已使用过的DateTimeFormatter
     */
    private static final ConcurrentHashMap<String, DateTimeFormatter> FORMATTER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
    /**
     * Number of nanos in a standard milliseconds.
     */
    public static final long NANOS_PER_MILLISECOND = 1000_000;

    /**
     * Number of milliseconds in a standard second.
     */
    public static final long MILLIS_PER_SECOND = 1000;

    /**
     * Number of milliseconds in a standard minute.
     */
    public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;

    /**
     * Number of milliseconds in a standard hour.
     */
    public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;

    /**
     * Number of milliseconds in a standard day.
     */
    public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

    /**
     ** 广电标准中的一天营业日期的开始时间
     */
    public static final int BUSINESS_START_HOUR = 6;

    /**
     ** 分隔符：空格
     */
    public static final String SPLITTER_SPACE = " ";

    /**
     ** 分隔符：T
     */
    public static final String SPLITTER_T = "'T'";

    /**
     ** 分隔符：-
     */
    public static final String SPLITTER_DASH = "-";

    /**
     ** 分隔符：/
     */
    public static final String SPLITTER_SLASH = "/";

    /**
     ** 分隔符：:
     */
    public static final String SPLITTER_COLON = ":";

    /**
     ** 分隔符：.
     */
    public static final String SPLITTER_DOT = ".";

    /**
     **年份，使用 yyyy 表示的年份，如 2011
     */
    public static final String PATTERN_YEARS = "yyyy";

    /**
     **年份，使用 yy 表示的年扮，如 11；
     */
    public static final String PATTERN_YEARS_SHORT = "yy";

    /**
     **月份，使用 MM 表示的月份，如 05；
     */
    public static final String PATTERN_MONTHS = "MM";

    /**
     **月份中的天数，使用 dd 表示的天数，如 10
     */
    public static final String PATTERN_DAYS = "dd";

    /**
     **一天中的小时数（0~23)，使用 HH 表示的小时数，如 18
     */
    public static final String PATTERN_HOURS = "HH";

    /**
     **一天中的小时数（1~12)，使用 hh 表示的小时数，如 10 (注意 10 有可能是 10 点，也可能是 22 点）
     */
    public static final String PATTERN_HOURS_SHORT = "hh";

    /**
     **分钟数，使用 mm 表示的分钟数，如 29
     */
    public static final String PATTERN_MINUTES = "mm";

    /**
     **秒数，使用 ss 表示的秒数，如 38
     */
    public static final String PATTERN_SECONDS = "ss";

    /**
     **毫秒数，使用 SSS 表示的毫秒数，如 156
     */
    public static final String PATTERN_MILLISECONDS = "SSS";

    /**
     ** 格式：yyyyMMdd
     */
    public static final String PATTERN_YYYYMMDD_UNION = "yyyyMMdd";

    /**
     ** 格式：yyyy-MM
     */
    public static final String PATTERN_YYYYMM = "yyyy-MM";

    /**
     ** 格式：yyyy-MM-dd
     */
    public static final String PATTERN_YYYYMMDD = "yyyy-MM-dd";

    /**
     ** 格式：yyyy/MM/dd
     */
    public static final String PATTERN_YYYYMMDD_SLASH = "yyyy/MM/dd";

    /**
     ** 格式：yyyy年MM月dd日
     */
    public static final String PATTERN_YYYYMMDD_CN = "yyyy年MM月dd日";

    /**
     ** 格式：HHmmss
     */
    public static final String PATTERN_HHMMSS_UNION = "HHmmss";

    /**
     ** 格式：HH:mm:ss
     */
    public static final String PATTERN_HHMMSS = "HH:mm:ss";

    /**
     ** 格式：HH:mm
     */
    public static final String PATTERN_HHMM = "HH:mm";

    /**
     ** 格式：HH时mm分ss秒
     */
    public static final String PATTERN_HHMMSS_CN = "HH时mm分ss秒";

    /**
     ** 格式：yyyyMMddHHmmss
     */
    public static final String PATTERN_YYYYMMDDHHMMSS_UNION = PATTERN_YYYYMMDD_UNION + PATTERN_HHMMSS_UNION;

    /**
     ** 格式：yyyy-MM-dd HH:mm:ss
     */
    public static final String PATTERN_YYYYMMDDHHMMSS = PATTERN_YYYYMMDD + SPLITTER_SPACE + PATTERN_HHMMSS;

    /**
     ** 格式：yyyy-MM-dd HH:mm:ss.SSS
     */
    public static final String PATTERN_YYYYMMDDHHMMSSZZZ = PATTERN_YYYYMMDD + SPLITTER_SPACE + PATTERN_HHMMSS + SPLITTER_DOT + PATTERN_MILLISECONDS;

    /**
     ** 格式：yyyy-MM-dd HH:mm
     */
    public static final String PATTERN_YYYYMMDDHHMM = PATTERN_YYYYMMDD + SPLITTER_SPACE + PATTERN_HHMM;

    /**
     ** 格式：yyyy-MM-dd'T'HH:mm:ss
     */
    public static final String PATTERN_YYYYMMDDHHMMSS_T = PATTERN_YYYYMMDD + SPLITTER_T + PATTERN_HHMMSS;

    /**
     ** 格式：yyyy年MM月dd日HH时mm分ss秒
     */
    public static final String PATTERN_YYYYMMDDHHMMSS_CN = PATTERN_YYYYMMDD_CN + SPLITTER_SPACE + PATTERN_HHMMSS_CN;

    /**
     ** 日期格式化：yyyy-MM-dd
     */
    public static final DateTimeFormatter FORMATTER_YYYYMMDD;
    static {
        FORMATTER_YYYYMMDD = new DateTimeFormatterBuilder().appendPattern(PATTERN_YYYYMMDD).toFormatter();
    }

    /**
     ** 日期格式化：HH:mm:ss
     */
    public static final DateTimeFormatter FORMATTER_HHMMSS;
    static {
        FORMATTER_HHMMSS = new DateTimeFormatterBuilder().appendPattern(PATTERN_HHMMSS).toFormatter();
    }

    /**
     ** 日期格式化：yyyy-MM-dd HH:mm:ss
     */
    public static final DateTimeFormatter FORMATTER_YYYYMMDDHHMMSS;
    static {
        FORMATTER_YYYYMMDDHHMMSS = new DateTimeFormatterBuilder().appendPattern(PATTERN_YYYYMMDDHHMMSS).toFormatter();
    }

    /**
     ** 日期格式化：yyyy-MM-dd HH:mm:ss.SSS
     */
    public static final DateTimeFormatter FORMATTER_YYYYMMDDHHMMSSZZZ;
    static {
        FORMATTER_YYYYMMDDHHMMSSZZZ = new DateTimeFormatterBuilder().appendPattern(PATTERN_YYYYMMDDHHMMSSZZZ).toFormatter();
    }

    /**
     * The datetime.
     */
    private LocalDateTime localDateTime;

    /**
     * Constructor.
     * Obtains the current date-time from the system clock in the default time-zone.
     */
    public EasyDate(){
        this.localDateTime = LocalDateTime.now();
    }

    /**
     * Constructor.
     */
    private EasyDate(LocalDateTime localDateTime){
        if(localDateTime == null){
            this.localDateTime = LocalDateTime.now();
        }else {
            this.localDateTime = localDateTime;
        }
    }

    /**
     ** 获取格式化类，如果缓存中有则直接返回，否则保存到缓存中，下次获取更方便
     *
     * @param pattern 格式字符串
     * @return DateTimeFormatter
     */
    public static DateTimeFormatter getFormatter(final String pattern){
        Objects.requireNonNull(pattern);
        switch (pattern) {
            case PATTERN_YYYYMMDD:
                return FORMATTER_YYYYMMDD;
            case PATTERN_HHMMSS:
                return FORMATTER_HHMMSS;
            case PATTERN_YYYYMMDDHHMMSS:
                return FORMATTER_YYYYMMDDHHMMSS;
            case PATTERN_YYYYMMDDHHMMSSZZZ:
                return FORMATTER_YYYYMMDDHHMMSSZZZ;
            default:
                DateTimeFormatter formatter = FORMATTER_CONCURRENT_HASH_MAP.get(pattern);
                if (formatter == null) {
                    formatter = DateTimeFormatter.ofPattern(pattern);
                    FORMATTER_CONCURRENT_HASH_MAP.put(pattern, formatter);
                }
                return formatter;
        }

    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 获取当前日期时间
     * Obtains the current date-time from the system clock in the default time-zone.
     *
     * @return The current date-time
     */
    public static EasyDate now(){
        return new EasyDate();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间字符串创建EasyDate实例
     * Obtains an instance of {@code EasyDate} from default pattern [yyyy-MM-dd] or [yyyy-MM-dd HH:mm:ss]
     *
     * @param text 日期时间字符串，格式：yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss
     * @return the local date-time, not null
     * @throws IllegalArgumentException if the string not match
     */
    public static EasyDate of(final String text){
        if(text == null){
            return new EasyDate();
        }else if(isDate(text)){
            return new EasyDate(LocalDate.parse(text, FORMATTER_YYYYMMDD).atStartOfDay());
        }else if(isDateTime(text)){
            return new EasyDate(LocalDateTime.parse(text, FORMATTER_YYYYMMDDHHMMSS));
        }else{
            throw new IllegalArgumentException("The text [" + text +
                    "] not match the default pattern [yyyy-MM-dd] or [yyyy-MM-dd HH:mm:ss]");
        }
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间字符串、日期时间格式创建EasyDate实例
     *
     * @param text 日期时间字符串
     * @param pattern 日期时间格式
     * @return the date-time，not null
     * @throws NullPointerException if {@code text} or {@code pattern} is {@code null}
     * @throws IllegalArgumentException if the pattern is invalid
     */
    public static EasyDate of(final String text, final String pattern){
        switch (pattern) {
            case PATTERN_YYYYMMDD:
                return new EasyDate(LocalDate.parse(text, FORMATTER_YYYYMMDD).atStartOfDay());
            case PATTERN_YYYYMMDDHHMMSS:
                return new EasyDate(LocalDateTime.parse(text, FORMATTER_YYYYMMDDHHMMSS));
            default:
                LocalDateTime localDateTime = LocalDateTime.now();
                TemporalAccessor accessor = getFormatter(pattern).parse(text);
                int year = accessor.isSupported(ChronoField.YEAR) ?
                        accessor.get(ChronoField.YEAR) : localDateTime.getYear();
                int month = accessor.isSupported(ChronoField.MONTH_OF_YEAR) ?
                        accessor.get(ChronoField.MONTH_OF_YEAR) : localDateTime.getMonthValue();
                int dayOfMonth = accessor.isSupported(ChronoField.DAY_OF_MONTH) ?
                        accessor.get(ChronoField.DAY_OF_MONTH) : localDateTime.getDayOfMonth();
                int hour = accessor.isSupported(ChronoField.HOUR_OF_DAY) ?
                        accessor.get(ChronoField.HOUR_OF_DAY) : localDateTime.getHour();
                int minute = accessor.isSupported(ChronoField.MINUTE_OF_HOUR) ?
                        accessor.get(ChronoField.MINUTE_OF_HOUR) : localDateTime.getMinute();
                int second = accessor.isSupported(ChronoField.SECOND_OF_MINUTE) ?
                        accessor.get(ChronoField.SECOND_OF_MINUTE) : localDateTime.getSecond();
                int milli = accessor.isSupported(ChronoField.MILLI_OF_SECOND) ?
                        accessor.get(ChronoField.MILLI_OF_SECOND) : 0;
                return of(year, month, dayOfMonth, hour, minute, second).withMilliseconds(milli);
        }
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间的毫秒数创建EasyDate实例
     *
     * @param millis 日期时间的毫秒数
     * @return the date-time, not null
     */
    public static EasyDate of(long millis){
        return new EasyDate(Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault()).toLocalDateTime());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据年月日创建EasyDate实例
     *
     * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month  the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth  the day-of-month to represent, from 1 to 31
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range,
     *  or if the day-of-month is invalid for the month-year
     */
    public static EasyDate of(int year, int month, int dayOfMonth){
        return new EasyDate(LocalDate.of(year, month, dayOfMonth).atStartOfDay());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据年月日时分秒创建EasyDate实例
     *
     * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month  the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth  the day-of-month to represent, from 1 to 31
     * @param hour  the hour-of-day to represent, from 0 to 23
     * @param minute  the minute-of-hour to represent, from 0 to 59
     * @param second  the second-of-minute to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range,
     *  or if the day-of-month is invalid for the month-year
     */
    public static EasyDate of(int year, int month, int dayOfMonth, int hour, int minute, int second){
        return new EasyDate(LocalDateTime.of(year, month, dayOfMonth, hour, minute, second));
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间创建EasyDate实例
     *
     * @param date 日期时间
     * @return the date-time, not null
     * @throws NullPointerException if {@code date} is {@code null}
     */
    public static EasyDate of(Instant date){
        validateDateNotNull(date);
        return new EasyDate(date.atZone(ZoneId.systemDefault()).toLocalDateTime());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间创建EasyDate实例
     *
     * @param date 日期时间
     * @return the date-time, not null
     * @throws NullPointerException if {@code date} is {@code null}
     */
    public static EasyDate of(Calendar date){
        validateDateNotNull(date);
        return new EasyDate(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间创建EasyDate实例
     *
     * @param date 日期时间
     * @return the date-time, not null
     * @throws NullPointerException if {@code date} is {@code null}
     */
    public static EasyDate of(Date date){
        return new EasyDate(asLocalDateTime(date));
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间创建EasyDate实例
     *
     * @param date 日期时间
     * @return the date-time, not null
     * @throws NullPointerException if {@code date} is {@code null}
     */
    public static EasyDate of(LocalDate date){
        validateDateNotNull(date);
        return new EasyDate(date.atStartOfDay());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 根据日期时间创建EasyDate实例
     *
     * @param date 日期时间
     * @return the date-time, not null
     * @throws NullPointerException if {@code date} is {@code null}
     */
    public static EasyDate of(LocalDateTime date){
        validateDateNotNull(date);
        return new EasyDate(date);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     **判断字符串是否日期格式：yyyy-MM-dd
     *
     * @param text 日期字符串
     * @return boolean
     */
    public static boolean isDate(final CharSequence text){
        return isDateTimeFormat(text, PATTERN_YYYYMMDD);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     **判断字符串是否日期时间格式：yyyy-MM-dd HH:mm:ss
     *
     * @param text 日期时间字符串
     * @return boolean
     */
    public static boolean isDateTime(final CharSequence text){
        return isDateTimeFormat(text, PATTERN_YYYYMMDDHHMMSS);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 判断字符串是否日期格式
     *
     * @param text 日期字符串
     * @param pattern 格式
     * @return boolean
     */
    public static boolean isDateTimeFormat(final CharSequence text, final String pattern){
        if(text == null || pattern == null){
            return false;
        }
        try {
            TemporalAccessor accessor = getFormatter(pattern).parse(text);
            return accessor.isSupported(ChronoField.YEAR)
                    || accessor.isSupported(ChronoField.MONTH_OF_YEAR)
                    || accessor.isSupported(ChronoField.DAY_OF_MONTH)
                    || accessor.isSupported(ChronoField.HOUR_OF_DAY)
                    || accessor.isSupported(ChronoField.MINUTE_OF_HOUR)
                    || accessor.isSupported(ChronoField.SECOND_OF_MINUTE)
                    || accessor.isSupported(ChronoField.MILLI_OF_SECOND);
        } catch (Exception e) {
            return false;
        }
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 日期转换
     *
     * @param date 日期
     * @return Date
     * @throws IllegalArgumentException if the date is null
     */
    public static Date asDate(final LocalDate date) {
        validateDateNotNull(date);
        return Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 日期时间转换
     *
     * @param date 日期时间
     * @return Date
     * @throws IllegalArgumentException if the date is null
     */
    public static Date asDate(final LocalDateTime date) {
        validateDateNotNull(date);
        return Date.from(date.atZone(ZoneId.systemDefault()).toInstant());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 日期转换
     *
     * @param date 日期时间
     * @return LocalDate
     * @throws IllegalArgumentException if the date is null
     */
    public static LocalDate asLocalDate(final Date date) {
        validateDateNotNull(date);
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 日期时间转换
     *
     * @param date 日期时间
     * @return LocalDateTime
     * @throws IllegalArgumentException if the date is null
     */
    public static LocalDateTime asLocalDateTime(final Date date) {
        validateDateNotNull(date);
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     * Converts a {@code Date} into a {@code Calendar}.
     *
     * @param date the date to convert to a Calendar
     * @return the created Calendar
     * @throws IllegalArgumentException if the date is null
     */
    public static Calendar asCalendar(final Date date) {
        validateDateNotNull(date);
        final Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上年数
     *
     * @param years  the years to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusYears(final long years) {
        localDateTime = localDateTime.plusYears(years);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去年数
     *
     * @param years  the years to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusYears(final long years) {
        localDateTime = localDateTime.minusYears(years);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上月数
     *
     * @param months  the months to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusMonths(final long months) {
        localDateTime = localDateTime.plusMonths(months);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去月数
     *
     * @param months  the months to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusMonths(final long months) {
        localDateTime = localDateTime.minusMonths(months);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上星期数
     *
     * @param weeks  the weeks to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusWeeks(final long weeks) {
        localDateTime = localDateTime.plusWeeks(weeks);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去星期数
     *
     * @param weeks  the weeks to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusWeeks(final long weeks) {
        localDateTime = localDateTime.minusWeeks(weeks);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上天数
     *
     * @param days  the days to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusDays(final long days) {
        localDateTime = localDateTime.plusDays(days);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去天数
     *
     * @param days  the days to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusDays(final long days) {
        localDateTime = localDateTime.minusDays(days);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上小时数
     *
     * @param hours  the hours to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusHours(final long hours) {
        localDateTime = localDateTime.plusHours(hours);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去小时数
     *
     * @param hours  the hours to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusHours(final long hours) {
        localDateTime = localDateTime.minusHours(hours);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上分钟数
     *
     * @param minutes  the minutes to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusMinutes(final long minutes) {
        localDateTime = localDateTime.plusMinutes(minutes);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去分钟数
     *
     * @param minutes  the minutes to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusMinutes(final long minutes) {
        localDateTime = localDateTime.minusMinutes(minutes);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上秒数
     *
     * @param seconds  the seconds to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusSeconds(final long seconds) {
        localDateTime = localDateTime.plusSeconds(seconds);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去秒数
     *
     * @param seconds  the seconds to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusSeconds(final long seconds){
        localDateTime = localDateTime.minusSeconds(seconds);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 加上毫秒数
     *
     * @param milliseconds  the milliseconds to add, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate plusMilliseconds(final long milliseconds) {
        localDateTime = localDateTime.plus(milliseconds, ChronoUnit.MILLIS);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 减去毫秒数
     *
     * @param milliseconds  the milliseconds to subtract, may be negative
     * @return this EasyDate instance
     * @throws DateTimeException if the result exceeds the supported date range
     */
    public EasyDate minusMilliseconds(final long milliseconds){
        localDateTime = localDateTime.minus(milliseconds, ChronoUnit.MILLIS);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定年份
     *
     * @param year  the year to set in the result, from MIN_YEAR to MAX_YEAR
     * @return this EasyDate instance
     * @throws DateTimeException if the year value is invalid
     */
    public EasyDate withYear(final int year) {
        localDateTime = localDateTime.withYear(year);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定年份
     *
     * @param year  the year to set in the result, from MIN_YEAR to MAX_YEAR
     * @return this EasyDate instance
     * @throws DateTimeException if the year value is invalid
     */
    public EasyDate withYear(final long year) {
        YEAR_OF_ERA.checkValidValue(year);
        return withYear(Long.valueOf(year).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份
     *
     * @param month  the month-of-year to set in the result, from 1 (January) to 12 (December)
     * @return this EasyDate instance
     * @throws DateTimeException if the month-of-year value is invalid
     */
    public EasyDate withMonth(final int month) {
        localDateTime = localDateTime.withMonth(month);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份
     *
     * @param month  the month-of-year to set in the result, from 1 (January) to 12 (December)
     * @return this EasyDate instance
     * @throws DateTimeException if the month-of-year value is invalid
     */
    public EasyDate withMonth(final long month) {
        MONTH_OF_YEAR.checkValidValue(month);
        return withMonth(Long.valueOf(month).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份中的天数
     *
     * @param dayOfMonth  the day-of-month to set in the result, from 1 to 28-31
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the day-of-month value is invalid,
     *  or if the day-of-month is invalid for the month-year
     */
    public EasyDate withDayOfMonth(final int dayOfMonth) {
        localDateTime = localDateTime.withDayOfMonth(dayOfMonth);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份中的天数
     *
     * @param dayOfMonth  the day-of-month to set in the result, from 1 to 28-31
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the day-of-month value is invalid,
     *  or if the day-of-month is invalid for the month-year
     */
    public EasyDate withDayOfMonth(final long dayOfMonth) {
        DAY_OF_MONTH.checkValidValue(dayOfMonth);
        return withDayOfMonth(Long.valueOf(dayOfMonth).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定年份中的天数
     *
     * @param dayOfYear  the day-of-year to set in the result, from 1 to 365-366
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the day-of-year value is invalid,
     *  or if the day-of-year is invalid for the year
     */
    public EasyDate withDayOfYear(final int dayOfYear) {
        localDateTime = localDateTime.withDayOfYear(dayOfYear);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定年份中的天数
     *
     * @param dayOfYear  the day-of-year to set in the result, from 1 to 365-366
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the day-of-year value is invalid,
     *  or if the day-of-year is invalid for the year
     */
    public EasyDate withDayOfYear(final long dayOfYear) {
        DAY_OF_YEAR.checkValidValue(dayOfYear);
        return withDayOfYear(Long.valueOf(dayOfYear).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定小时数
     *
     * @param hour  the hour-of-day to set in the result, from 0 to 23
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the hour value is invalid
     */
    public EasyDate withHour(final int hour) {
        localDateTime = localDateTime.withHour(hour);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定小时数
     *
     * @param hour  the hour-of-day to set in the result, from 0 to 23
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the hour value is invalid
     */
    public EasyDate withHour(final long hour) {
        HOUR_OF_DAY.checkValidValue(hour);
        return withHour(Long.valueOf(hour).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定分钟数
     *
     * @param minute  the minute-of-hour to set in the result, from 0 to 59
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the minute value is invalid
     */
    public EasyDate withMinute(final int minute) {
        localDateTime = localDateTime.withMinute(minute);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定分钟数
     *
     * @param minute  the minute-of-hour to set in the result, from 0 to 59
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the minute value is invalid
     */
    public EasyDate withMinute(final long minute) {
        MINUTE_OF_HOUR.checkValidValue(minute);
        return withMinute(Long.valueOf(minute).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定秒数
     *
     * @param second  the second-of-minute to set in the result, from 0 to 59
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the second value is invalid
     */
    public EasyDate withSecond(final int second) {
        localDateTime = localDateTime.withSecond(second);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定秒数
     *
     * @param second  the second-of-minute to set in the result, from 0 to 59
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the second value is invalid
     */
    public EasyDate withSecond(final long second) {
        return withSecond(Long.valueOf(second).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定毫秒数
     *
     * @param millisecond  the MilliOfSecond to set in the result, from 0 to 999
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the millisecond value is invalid
     */
    public EasyDate withMilliseconds(final int millisecond) {
        MILLI_OF_SECOND.checkValidValue(millisecond);
        int nanos = Long.valueOf(NANOS_PER_MILLISECOND).intValue();
        localDateTime = localDateTime.withNano(millisecond*nanos);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定毫秒数
     *
     * @param millisecond  the MilliOfSecond to set in the result, from 0 to 999
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the millisecond value is invalid
     */
    public EasyDate withMilliseconds(final long millisecond) {
        MILLI_OF_SECOND.checkValidValue(millisecond);
        return withMilliseconds(Long.valueOf(millisecond).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定纳秒数
     *
     * @param nanoOfSecond  the nano-of-second to set in the result, from 0 to 999,999,999
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the nano value is invalid
     */
    public EasyDate withNano(final int nanoOfSecond) {
        localDateTime = localDateTime.withNano(nanoOfSecond);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定纳秒数
     *
     * @param nanoOfSecond  the nano-of-second to set in the result, from 0 to 999,999,999
     * @return this EasyDate instance, not null
     * @throws DateTimeException if the nano value is invalid
     */
    public EasyDate withNano(final long nanoOfSecond) {
        NANO_OF_SECOND.checkValidValue(nanoOfSecond);
        return withNano(Long.valueOf(nanoOfSecond).intValue());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份为最小值，即: 1
     *
     * @return this EasyDate instance
     */
    public EasyDate withMinMonth() {
        return withMonth(1);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份中的天数为最小值，即: 1
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMinDay() {
        return withDayOfMonth(1);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定小时数为最小值，即: 0
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMinHour() {
        return withHour(0);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定分钟数为最小值，即: 0
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMinMinute() {
        return withMinute(0);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定秒数为最小值，即: 0
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMinSecond() {
        return withSecond(0);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定毫秒数为最小值，即: 0
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMinMilliseconds() {
        return withMilliseconds(0);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定时分秒为最小值，即: 00:00:00
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMinHHMMSS() {
        return withMinHour().withMinMinute().withMinSecond();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定时分秒毫秒为最小值，即: 00:00:00.000
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMinHHMMSSZZZ() {
        return withMinHHMMSS().withMinMilliseconds();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份为最大值，即: 12
     *
     * @return this EasyDate instance
     */
    public EasyDate withMaxMonth() {
        return withMonth(12);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定月份中的天数为最大值，即: 28-31
     * <p>
     * 1月31天；
     * 2月闰年29天，非闰年28天；
     * 3月31天；
     * 4月30天；
     * 5月31天；
     * 6月30天；
     * 7月31天；
     * 8月31天；
     * 9月30天；
     * 10月31天；
     * 11月30天；
     * 12月31天；
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMaxDay() {
        int dom = 31;
        switch (localDateTime.getMonthValue()) {
            case 2:
                dom = (IsoChronology.INSTANCE.isLeapYear(localDateTime.getYear()) ? 29 : 28);
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                dom = 30;
                break;
            default:
        }
        return withDayOfMonth(dom);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定小时数为最大值，即: 23
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMaxHour() {
        return withHour(23);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定分钟数为最大值，即: 59
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMaxMinute() {
        return withMinute(59);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定秒数为最大值，即: 59
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMaxSecond() {
        return withSecond(59);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定毫秒数为最大值，即: 999
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMaxMilliseconds() {
        return withMilliseconds(999);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定时分秒为最大值，即: 23:59:59
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMaxHHMMSS() {
        return withMaxHour().withMaxMinute().withMaxSecond();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定时分秒毫秒为最大值，即: 23:59:59.999
     *
     * @return this EasyDate instance, not null
     */
    public EasyDate withMaxHHMMSSZZZ() {
        return withMaxHHMMSS().withMaxMilliseconds();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 指定为营业时间，即当天6点到第二天6点
     *
     * @param isEnd 是否营业结束时间
     * @return a EasyDate of business date-time
     */
    public EasyDate withBusinessDateTime(final boolean isEnd){
        int hour = localDateTime.getHour();
        if (hour < BUSINESS_START_HOUR && !isEnd) {
            localDateTime = localDateTime.minusDays(1);
        } else if (hour >= BUSINESS_START_HOUR && isEnd) {
            localDateTime = localDateTime.plusDays(1);
        }
        localDateTime = localDateTime.withHour(BUSINESS_START_HOUR);
        localDateTime = localDateTime.withMinute(0);
        localDateTime = localDateTime.withSecond(0);
        localDateTime = localDateTime.withNano(0);
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 最小日期
     *
     * @param dates 比较日期，可多个
     * @return min date-time EasyDate
     */
    public EasyDate min(final Date ...dates){
        if(dates == null){
            return this;
        }
        for (int i = 0; i < dates.length; i++) {
            if(dates[i] == null){
                continue;
            }
            LocalDateTime other = asLocalDateTime(dates[i]);
            if(localDateTime.isAfter(other)){
                localDateTime = other;
            }
        }
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 最小日期
     *
     * @param dates 比较日期，可多个
     * @return min date-time EasyDate
     */
    public EasyDate min(final LocalDate ...dates){
        if(dates == null){
            return this;
        }
        for (int i = 0; i < dates.length; i++) {
            if(dates[i] == null){
                continue;
            }
            if(localDateTime.isAfter(dates[i].atStartOfDay())){
                localDateTime = dates[i].atStartOfDay();
            }
        }
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 最小日期
     *
     * @param dates 比较日期，可多个
     * @return min date-time EasyDate
     */
    public EasyDate min(final LocalDateTime ...dates){
        if(dates == null){
            return this;
        }
        for (int i = 0; i < dates.length; i++) {
            if(dates[i] == null){
                continue;
            }
            if(localDateTime.isAfter(dates[i])){
                localDateTime = dates[i];
            }
        }
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 最大日期
     *
     * @param dates 比较日期，可多个
     * @return max date-time EasyDate
     */
    public EasyDate max(final Date ...dates){
        if(dates == null){
            return this;
        }
        for (int i = 0; i < dates.length; i++) {
            if(dates[i] == null){
                continue;
            }
            LocalDateTime other = asLocalDateTime(dates[i]);
            if(localDateTime.isBefore(other)){
                localDateTime = other;
            }
        }
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 最大日期
     *
     * @param dates 比较日期，可多个
     * @return max date-time EasyDate
     */
    public EasyDate max(final LocalDate ...dates){
        if(dates == null){
            return this;
        }
        for (int i = 0; i < dates.length; i++) {
            if(dates[i] == null){
                continue;
            }
            if(localDateTime.isBefore(dates[i].atStartOfDay())){
                localDateTime = dates[i].atStartOfDay();
            }
        }
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 最大日期
     *
     * @param dates 比较日期，可多个
     * @return max date-time EasyDate
     */
    public EasyDate max(final LocalDateTime ...dates){
        if(dates == null){
            return this;
        }
        for (int i = 0; i < dates.length; i++) {
            if(dates[i] == null){
                continue;
            }
            if(localDateTime.isBefore(dates[i])){
                localDateTime = dates[i];
            }
        }
        return this;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 验证日期是否为空，如果为空则抛出异常
     *
     * @param date 日期
     * @throws NullPointerException if {@code date} is {@code null}
     */
    private static void validateDateNotNull(final Object date) {
        Objects.requireNonNull(date, "date");
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为日期类
     *
     * @return The date-time
     */
    public Date toDate(){
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     * Converts a {@code Date} into a {@code Calendar}.
     *
     * @return the created Calendar
     */
    public Calendar toCalendar() {
        final Calendar c = Calendar.getInstance();
        c.setTimeInMillis(toEpochMilli());
        return c;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为日期类
     *
     * @return The date-time
     */
    public LocalDate toLocalDate(){
        return localDateTime.toLocalDate();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     * Gets the {@code LocalTime} part of this date-time.
     * <p>
     * This returns a {@code LocalTime} with the same hour, minute, second and
     * nanosecond as this date-time.
     *
     * @return the time part of this date-time, not null
     */
    public LocalTime toLocalTime() {
        return localDateTime.toLocalTime();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为日期类
     *
     * @return The date-time
     */
    public LocalDateTime toLocalDateTime(){
        return localDateTime;
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     * Converts this date-time to the number of milliseconds from the epoch of 1970-01-01T00:00:00Z.
     *
     * @return milliseconds
     */
    public long toEpochMilli(){
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为字符串，格式为：yyyy-MM-dd HH:mm:ss.SSS
     *
     * @return The date-time
     */
    @Override
    public String toString(){
        return localDateTime.format(FORMATTER_YYYYMMDDHHMMSSZZZ);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为指定格式的字符串
     *
     * @param pattern 格式
     * @return The date-time
     * @throws DateTimeException if an error occurs during printing
     * @see #getFormatter(String)
     */
    public String toString(final String pattern){
        return localDateTime.format(getFormatter(pattern));
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为字符串，格式为：yyyy-MM-dd
     *
     * @return The date-time
     */
    public String toDateString(){
        return localDateTime.format(FORMATTER_YYYYMMDD);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为字符串，格式为：yyyy-MM-dd
     *
     * @return The date-time
     */
    public String toTimeString(){
        return localDateTime.format(FORMATTER_HHMMSS);
    }

    //-----------------------------------------------------------------------------------------------------------------
    /**
     ** 转换为字符串，格式为：yyyy-MM-dd HH:mm:ss
     *
     * @return The date-time
     */
    public String toDateTimeString(){
        return localDateTime.format(FORMATTER_YYYYMMDDHHMMSS);
    }

}
